home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / libs / mthr25 / mthread.c < prev    next >
C/C++ Source or Header  |  1995-03-29  |  29KB  |  1,085 lines

  1. /* MicroThread V2.5
  2.  
  3.   A minimal Borland/Turbo C multithreading library for DOS program.
  4.   Written and placed into the public domain by I H Ting. Please read
  5.   the files README.TXT and MTHREAD.TXT for more information.
  6.  
  7.   Comments and suggestions to :
  8.  
  9.       Internet Email : i.h.ting@wlv.ac.uk
  10.  
  11.       Compuserve     : 100023,3363
  12.  
  13.       Otherwise      : I H Ting
  14.                        University of Wolverhampton
  15.                        School of Computing & I. T.
  16.                        Wulfruna Street
  17.                        Wolverhampton WV1 1SB
  18.                        U.K.
  19. */
  20.  
  21. /* MTHREAD.C - Main file for the kernel functions. This is all you
  22.    need if you are not using any of Turbo C's runtime libraries. If you
  23.    do, then you will also need MTCRTLIB.C
  24. */
  25.  
  26.  
  27. #define MTHREAD_C 25
  28. #include "stdio.h"
  29. #include "stdarg.h"
  30. #include "dos.h"
  31. #include "conio.h"
  32. #include "stdlib.h"
  33. #include "mem.h"
  34. #include "mthread.h"
  35.  
  36. /* Clock intrreupt vector */
  37. #define TIMER_INT_VECT 8
  38.  
  39. /* DOS INT21 interrupt vector. Not used in this version */
  40. #define DOS21_INT_VECT 0x21
  41.  
  42. /* Maximum nesting for critical sections */
  43. #define MAX_CRITICAL_NESTING 64000L
  44.  
  45. /* Change these to suit your application */
  46. #define MAX_THREADS  40
  47. #define MAX_SEMAPHORES  20
  48. #define MAX_MAILBOXES  40
  49. #define MAILBOX_NAME_LEN 32
  50. #define THREAD_STACK_SIZE 8192
  51.  
  52. /* Macro wrappers to make it easier to use the general EnqueueThread,
  53.    DequeueThread and ExtractThread functions */
  54. #define EnqueueReady(ID) \
  55.   EnqueueThread(ID,&ReadyQHeads[threads[ID].priority], \
  56.                 &ReadyQTails[threads[ID].priority])
  57.  
  58. #define DequeueReady(priority) \
  59.   DequeueThread(&ReadyQHeads[priority],&ReadyQTails[priority])
  60.  
  61. #define ExtractReady(ID) \
  62.   ExtractThread(ID,&ReadyQHeads[threads[ID].priority], \
  63.                 &ReadyQTails[threads[ID].priority])
  64.  
  65.  
  66. #define EnqueueSema(threadID, semaID)\
  67.   EnqueueThread(curThreadID, \
  68.                 &(semaphores[semaID].QHead), \
  69.                 &(semaphores[semaID].QTail))
  70.  
  71. #define DequeueSema(semaID) \
  72.   DequeueThread(&(semaphores[semaID].QHead),&(semaphores[semaID].QTail))
  73.  
  74. #define ExtractSema(ID) \
  75.   ExtractThread(ID,&(semaphores[threads[ID].semaphore].QHead), \
  76.                 &(semaphores[threads[ID].semaphore].QTail))
  77.  
  78.  
  79. /* The various states of the threads */
  80. enum ThreadStatus {INVALID, CURRENT, READY, WAITING, SLEEPING, TERMINATED};
  81.  
  82. /* Structure to hold the CPU registers on the runtime stack */
  83. typedef struct {
  84.   unsigned bp;
  85.   unsigned di;
  86.   unsigned si;
  87.   unsigned ds;
  88.   unsigned es;
  89.   unsigned dx;
  90.   unsigned cx;
  91.   unsigned bx;
  92.   unsigned ax;
  93.   unsigned ip;
  94.   unsigned cs;
  95.   unsigned flags;
  96. } RegsOnStack;
  97.  
  98.  
  99. /* Thread execution context */
  100. typedef struct {
  101.   unsigned sp;  /* stack pointer */
  102.   unsigned ss;  /* stack segment */
  103.   unsigned priority;  /* priority: 1,2,3,4 or 5 */
  104.   enum ThreadStatus status;
  105.   unsigned semaphore; /* blocking semaphore */
  106.   void *pOwnStack;
  107.   PThreadFunc pFunc; /* pointer to the thread function */
  108.   void *pArg;   /* pointer argument passed to the thread function */
  109.   void *pMsg;
  110.   unsigned msgSize;
  111.   unsigned long wakeUpTime;  /* time to wake the sleeping thread up */
  112.   unsigned prevID; /* ID of previous thread in the linked-list queue */
  113.   unsigned nextID; /* ID of next thread in the linked-list queue */
  114. } ThreadContext;
  115.  
  116.  
  117. /* The Thread List - implemented as an array of thread execution contexts
  118.    so that the thread IDs correspond to the array indices */
  119. ThreadContext threads[MAX_THREADS];
  120.  
  121. /* Semaphores */
  122. typedef struct{
  123.   unsigned value; /* this is incremented on each wait */
  124.   unsigned inUse; /* Nonzero if in used, non-zero otherwise*/
  125.   unsigned QHead; /* head of queue of threads waiting on this semaphore*/
  126.   unsigned QTail; /* tail of above queue */
  127. }SemaphoreInfo;
  128.  
  129. /* Array of available semaphores, index is ID */
  130. SemaphoreInfo semaphores[MAX_SEMAPHORES];
  131.  
  132.  
  133. /* Mailboxes */
  134. typedef struct{
  135.   char name[MAILBOX_NAME_LEN];
  136.   unsigned numReceivers; /* num of receivers is waiting */
  137.   unsigned numSenders; /*num of senders waiting */
  138.   unsigned inUse; /* Nonzero if in used, non-zero otherwise*/
  139.   unsigned QHead; /* head of queue of threads waiting on this mbx*/
  140.   unsigned QTail; /* tail of above queue */
  141. } MailBoxInfo;
  142.  
  143. /* Array of available mailboxes */
  144. MailBoxInfo mailboxes[MAX_MAILBOXES];
  145.  
  146. /* Forward prototypes */
  147. void TheScheduler(void);
  148. void interrupt StopMultiThreading(void);
  149. void interrupt DispatchCurThread(void);
  150. void interrupt GotoScheduler(void);
  151. void interrupt (*PrevTimerIntHandler)(void);
  152. void WakeUpThreads(unsigned semaphore);
  153. void TidyUp(void);
  154. int  IsDeadLocked(void);
  155. void interrupt RealStart(void);
  156. void EnqueueThread(unsigned threadID, unsigned *pQHead, unsigned *pQTail);
  157. unsigned DequeueThread(unsigned *pQHead, unsigned *pQTail);
  158. void WakeUpSleepingThreads(void);
  159.  
  160.  
  161. /* Static varaiables for MicroThread management */
  162.  
  163. /* Various stack segment and pointer storage variables */
  164. static unsigned originalSS, originalSP,schedulerSS, schedulerSP;
  165.  
  166. /* The current thread ID */
  167. static unsigned curThreadID = 0;
  168.  
  169. /* lock count for critical sections */
  170. static unsigned iCriticalSection=0;
  171.  
  172. /* flag to indicate whether currently multithreading */
  173. static char bMultiThreading = 0;
  174.  
  175. /* is MicroThread initialised ? */
  176. static char bMTInitialised=0;
  177.  
  178. /* Ctrl-Break flag */
  179. static char bCtrlBreak=0;
  180.  
  181. /* The READY queues - one for each priority*/
  182. static unsigned ReadyQHeads[6], ReadyQTails[6];
  183.  
  184. static unsigned sleepQHead, sleepQTail;
  185.  
  186. /* MicroThread can be preemptive or non-preemptive,
  187.    This flag control the behaviour.
  188.    Use MTSetPreemptive() to change it */
  189. static char bMTPreemptive=1;
  190.  
  191. /* Mthread return code */
  192. static enum MTReturnCode returnCode=NORMAL;
  193.  
  194. /* The semaphore used to seralise access top C runtime library functions */
  195. Semaphore semaCRunTimeLib=0;
  196.  
  197. /* pointer to InDOS flag for checking to see if
  198.    we are in a dos service rountine somewhere */
  199. static char far * pInDosFlag;
  200.  
  201. /* clock ticks since MThread started */
  202. static unsigned long clockTick=0;
  203.  
  204. /*static unsigned long nextWakeUpTick=0;*/
  205.  
  206. /* Scheduling policy. Change ChooseNextThread() to change scheduling
  207.   policy */
  208. static unsigned scheduleMap[15]={5,4,3,5,4,2,5,3,4,5,1,4,5,3,2};
  209. static unsigned priorityIdx=15;
  210.  
  211. unsigned ChooseNextThread(void)
  212. {
  213.   unsigned nextID;
  214.   unsigned loopCounter=0;
  215.   while(1){
  216.     priorityIdx++;
  217.     if (priorityIdx >=15){
  218.       priorityIdx=0;
  219.       loopCounter++;
  220.     }
  221.     nextID = DequeueReady(scheduleMap[priorityIdx]);
  222.     if (nextID==0){
  223.       if(loopCounter>2) /* Gone round twice? */
  224.         return 0; /* No READY threads! Possibly deadlocked. */
  225.     }
  226.     else
  227.       return nextID;
  228.   }
  229. }
  230.  
  231. /* The scheduler itself */
  232. static void TheScheduler(void)
  233. {
  234.   unsigned nextThreadID;
  235.  
  236.   while(1){
  237.     /* Check that the current thrad is a valid one, if so
  238.        join it to the back of the READY queue */
  239.     if(curThreadID > 0 && threads[curThreadID].status==CURRENT){
  240.       threads[curThreadID].status = READY;
  241.       EnqueueReady(curThreadID);
  242.     }
  243.     /* check that the user hasn't hit Ctrl-Break */
  244.     if(bCtrlBreak){
  245.       bMultiThreading = 0;
  246.       returnCode = CTRLBREAK;
  247.     }
  248.     /* if any threads are sleeping, check and
  249.        wake up the apropriate threads */
  250.     if(sleepQHead){
  251.       WakeUpSleepingThreads();
  252.     }
  253.     /* Call ChooseNextThread to select a thread for dispatching */
  254.     if(bMultiThreading) {
  255.       nextThreadID = ChooseNextThread();
  256.       curThreadID = nextThreadID;
  257.       /* Check for deadlock if no threads are sleeping */
  258.       if (sleepQHead==0){
  259.         if(nextThreadID==0){ /* Deadlocked. End multi-threading */
  260.           bMultiThreading = 0;
  261.           returnCode = DEADLOCKED;
  262.         }
  263.         else {
  264.           curThreadID = nextThreadID;
  265.         }
  266.       }
  267.     }
  268.     /* Check to see if we are ending multi-threading */
  269.     if(!bMultiThreading) {
  270.       StopMultiThreading();
  271.     }
  272.     /* If a thread is found, switch to it */
  273.     if (nextThreadID) DispatchCurThread();
  274.   }
  275. }
  276.  
  277.  
  278. /* The dispatcher, switches from the scheduler to the current thread */
  279. static void interrupt DispatchCurThread(void)
  280. {
  281.   //putchar('T');
  282.   disable();
  283.   schedulerSS = _SS;
  284.   schedulerSP = _SP;
  285.   _SS = threads[curThreadID].ss;
  286.   _SP = threads[curThreadID].sp;
  287.   threads[curThreadID].status = CURRENT;
  288.   enable();
  289. }
  290.  
  291.  
  292. /* go from thread to scheduler */
  293. static void interrupt GotoScheduler(void)
  294. {
  295.   disable();
  296.   threads[curThreadID].ss = _SS;
  297.   threads[curThreadID].sp = _SP;
  298.   _SS = schedulerSS;
  299.   _SP = schedulerSP;
  300.   enable();
  301. }
  302.  
  303.  
  304. /* Our own Control-Break handler - it sets a flag
  305.    so that we know when to stop multi-threading */
  306. static int CtrlBreak(void)
  307. {
  308.    if (bMultiThreading) {
  309.      bCtrlBreak=1;
  310.      return 1;
  311.    }
  312.    else
  313.      return 0;
  314. }
  315.  
  316.  
  317. /* The replacement timer interrupt handler.
  318.    Switches from the current thread to the scheduler */
  319. static void interrupt OurTimerIntHandler(void)
  320. {
  321.   /* Saves the current thread's stack seg and pointer.
  322.      Set the stack segment and pointer to point to the scheduler's
  323.      and the scheduler will resume running after the timer interrupt */
  324.     clockTick++;
  325.     if (*pInDosFlag==0){ /* Are we running a DOS routine somewhere? */
  326.       if (iCriticalSection==0){ /* Are we in a critical section? */
  327.         if (_SS != schedulerSS ){ /* Are we already in our scheduler? */
  328.           if(_SS==threads[curThreadID].ss){/* Are we in a valid thread and*/
  329.             threads[curThreadID].sp = _SP; /* not a strange TSR somewhere?*/
  330.             _SS = schedulerSS;    /* None of the above! So we can */
  331.             _SP = schedulerSP;    /* switch to the scheduler */
  332.           }
  333.         }
  334.       }
  335.     }
  336.   (*PrevTimerIntHandler)(); /* Call the call the original interrupt handler */
  337. }
  338.  
  339.  
  340. /* Stick a thread to the back of a queue */
  341. void EnqueueThread(unsigned threadID, unsigned *pQHead, unsigned *pQTail)
  342. {
  343.   if (*pQHead ==0){ /* nothing in the queue */
  344.     *pQHead = threadID;
  345.     *pQTail = threadID;
  346.     threads[threadID].prevID=0;
  347.   }
  348.   else{
  349.     threads[*pQTail].nextID = threadID;
  350.     threads[threadID].prevID = *pQTail;
  351.     *pQTail = threadID;
  352.   }
  353.   threads[threadID].nextID=0;
  354. }
  355.  
  356.  
  357. /* take the thread at the head of the queue off  and return its ID */
  358. unsigned DequeueThread(unsigned *pQHead, unsigned *pQTail)
  359. {
  360.   unsigned threadID=0;
  361.  
  362.   if (*pQHead ==0){ /* nothing in the queue */
  363.     return 0;       /* error! */
  364.   }
  365.   else if (*pQHead == *pQTail){ /* one item in the queue */
  366.     threadID = *pQHead;
  367.     *pQHead = *pQTail = 0;
  368.   }
  369.   else {
  370.     threadID = *pQHead;
  371.     *pQHead = threads[*pQHead].nextID;
  372.     threads[*pQHead].prevID=0;
  373.   }
  374.   return threadID;
  375. }
  376.  
  377.  
  378. /* this function assumes that threadID is the queue.
  379.    If not, we are in trouble! */
  380. void ExtractThread(unsigned threadID, unsigned *pQHead, unsigned *pQTail)
  381. {
  382.   unsigned prevID=0, nextID=0;
  383.  
  384.   prevID = threads[threadID].prevID;
  385.   nextID = threads[threadID].nextID;
  386.   if (prevID==0){
  387.     *pQHead=nextID;
  388.   }
  389.   else {
  390.     threads[prevID].nextID = nextID;
  391.   }
  392.  
  393.   if (nextID==0){
  394.     *pQTail=prevID;
  395.   }
  396.   else {
  397.     threads[nextID].prevID = prevID;
  398.   }
  399. }
  400.  
  401.  
  402.  
  403. /* Big red button. Puts the original stack back,
  404.    and takes out out interrupt handler */
  405. static void interrupt StopMultiThreading(void)
  406. {
  407.   disable();
  408.   _SS = originalSS;
  409.   _SP = originalSP;
  410.   if (bMTPreemptive){
  411.     setvect(TIMER_INT_VECT, PrevTimerIntHandler);
  412.   }
  413.   enable();
  414. }
  415.  
  416.  
  417. /* Tidy up - return MicroThread to the uninitialised state.
  418.    A bit of an overkill, but better be safe than sorry! */
  419. static void TidyUp(void)
  420. {
  421.   int i;
  422.  
  423.   /* free up all the thread stacks */
  424.   for(i=0; i<MAX_THREADS; i++) {
  425.     if(threads[i].pOwnStack != NULL) {
  426.       free(threads[i].pOwnStack);
  427.       threads[i].pOwnStack = NULL;
  428.     }
  429.     if(threads[i].pArg != NULL) {
  430.       free(threads[i].pArg);
  431.       threads[i].pArg = NULL;
  432.     }
  433.     threads[i].status = INVALID;
  434.     threads[i].semaphore = NULL;
  435.     threads[i].ss=0;
  436.     threads[i].nextID=0;
  437.   }
  438.   for(i=0; i<MAX_SEMAPHORES; i++) {
  439.     semaphores[i].value=0;
  440.     semaphores[i].inUse=0;
  441.     semaphores[i].QHead=0;
  442.     semaphores[i].QTail=0;
  443.   }
  444.   for(i=1; i<=5; i++){
  445.     ReadyQHeads[i]=0;
  446.     ReadyQTails[i]=0;
  447.   }
  448.   curThreadID = 0;
  449.   iCriticalSection=0;
  450.   bMultiThreading = 0;
  451.   bMTInitialised=0;
  452.   bCtrlBreak=0;
  453.   semaCRunTimeLib=0;
  454. }
  455.  
  456.  
  457. /* Obvious, but not currently used in this version */
  458. static int IsDeadLocked(void)
  459. {
  460.   int i;
  461.   /* If there are no threads READY to run, we are DeadLocked */
  462.   for(i=1; i<MAX_THREADS; i++)
  463.     if (threads[i].status==READY) return 0;
  464.   return 1;
  465. }
  466.  
  467. /* A shell for the thread function. This is to allow us to pass arguments
  468.    to the thread function, and to call MTEndThread() when the thread
  469.    function returns */
  470. void ThreadShell(void)
  471. {
  472.   (*(threads[curThreadID].pFunc))(threads[curThreadID].pArg);
  473.   MTEndThread();
  474. }
  475.  
  476. static void CopyMem(char *pDest, char *pSource, unsigned nBytes)
  477. {
  478.  
  479.   unsigned i;
  480.   for(i=0; i<nBytes; i++){
  481.     *pDest++ = *pSource++;
  482.   }
  483. }
  484.  
  485. /* Adds a new thread. Can be used from a running thread.
  486.    MicroThread has to be already initialised */
  487. int MTAddNewThread(PThreadFunc pThreadFunc, unsigned priority,
  488.                    unsigned sizeArg, void *pArg)
  489. {
  490.   RegsOnStack *pRegs;
  491.   unsigned threadID, i;
  492.   void *pNewStack, *pArgMem;
  493.  
  494.   if (bMTInitialised){
  495.     MTWait(semaCRunTimeLib);
  496.     MTEnterCritical();
  497.     threadID = MAX_THREADS;
  498.     /* look for a useable slot in the thread list */
  499.     for(i=0; i<MAX_THREADS; i++){
  500.       if (threads[i].status == INVALID
  501.         ||threads[i].status == TERMINATED){
  502.         threadID = i;
  503.         break;
  504.       }
  505.     }
  506.     if (threadID == MAX_THREADS){  /* Failed */
  507.       MTLeaveCritical();
  508.       MTSignal(semaCRunTimeLib);
  509.       return 0;
  510.     }
  511.     /* allocate a new stack for the thread.*/
  512.     pNewStack = malloc(THREAD_STACK_SIZE + sizeof(RegsOnStack));
  513.     if (pNewStack==NULL){
  514.       MTLeaveCritical();
  515.       MTSignal(semaCRunTimeLib);
  516.       return 0;  /* failed */
  517.     }
  518.     if (sizeArg && pArg) {
  519.       pArgMem = malloc(sizeArg);
  520.       if (pArgMem==NULL){
  521.         free(pNewStack);
  522.         MTLeaveCritical();
  523.         MTSignal(semaCRunTimeLib);
  524.         return 0;  /* failed */
  525.       }
  526.       CopyMem(pArgMem, pArg, sizeArg);
  527.     }
  528.     else {
  529.       pArgMem=NULL;
  530.     }
  531.     threads[threadID].pOwnStack = pNewStack;
  532.     pRegs = (RegsOnStack *) threads[threadID].pOwnStack +
  533.             THREAD_STACK_SIZE - sizeof(RegsOnStack);
  534.     threads[threadID].sp = FP_OFF((RegsOnStack far *) pRegs);
  535.     threads[threadID].ss = FP_SEG((RegsOnStack far *) pRegs);
  536.     threads[threadID].pFunc = pThreadFunc;
  537.     threads[threadID].pArg = pArgMem;
  538.     pRegs->cs = FP_SEG(ThreadShell);
  539.     pRegs->ip = FP_OFF(ThreadShell);
  540.     pRegs->ds = _DS;
  541.     pRegs->es = _ES;
  542.     pRegs->flags = 0x200;
  543.     threads[threadID].priority = priority;
  544.     threads[threadID].status = READY;
  545.     EnqueueReady(threadID);
  546.     MTLeaveCritical();
  547.     MTSignal(semaCRunTimeLib);
  548.   }
  549.   return threadID;
  550. }
  551.  
  552.  
  553. /* Kicks off the system by setting up the stack pointer to point
  554.    to the scheduler, and patching in the timer interrupt vector
  555.    if it is in preemptive mode */
  556. static void interrupt RealStart(void)
  557. {
  558.  
  559.   originalSS = _SS;
  560.   originalSP = _SP;
  561.   _SS = schedulerSS;
  562.   _SP = schedulerSP;
  563.   if (bMTPreemptive){
  564.     disable();
  565.     PrevTimerIntHandler = getvect(TIMER_INT_VECT);
  566.     setvect(TIMER_INT_VECT, OurTimerIntHandler);
  567.     enable();
  568.   }
  569.   bMultiThreading=1;
  570.   returnCode = NORMAL;
  571. }
  572.  
  573.  
  574. /* A shell for RealStart, and takes care of tidying up
  575.    when multi-threading stops */
  576. enum MTReturnCode MTStartMultiThreading(void)
  577. {
  578.   union REGS regs;
  579.   struct SREGS sregs;
  580.  
  581.   regs.h.ah=0x34;
  582.   int86x(0x21,®s,®s,&sregs);
  583.   pInDosFlag=MK_FP(sregs.es, regs.x.bx);
  584.   setcbrk(1);
  585.   ctrlbrk(CtrlBreak);
  586.   if (bMTInitialised && !bMultiThreading){
  587.     RealStart();
  588.     TidyUp();
  589.     return returnCode;
  590.   }
  591.   else
  592.     return NOT_INITIALISED;
  593. }
  594.  
  595.  
  596.  
  597. /* Stops a running thread and frees its stack */
  598. void MTKillThread(unsigned threadID)
  599. {
  600.   MTWait(semaCRunTimeLib);
  601.   MTEnterCritical();
  602.   if(bMultiThreading && threadID <MAX_THREADS && threadID > 0){
  603.     if(threadID != curThreadID){
  604.       if(threads[threadID].status==READY){
  605.         ExtractReady(threadID);
  606.       }
  607.       else if(threads[threadID].status==WAITING){
  608.         ExtractSema(threadID);
  609.       }
  610.     }
  611.     threads[threadID].status = TERMINATED;
  612.     if(threads[threadID].pOwnStack)
  613.       free(threads[threadID].pOwnStack);
  614.     threads[threadID].pOwnStack = NULL;
  615.     threads[threadID].ss=0;
  616.     if(threads[threadID].pArg)
  617.       free(threads[threadID].pArg);
  618.     threads[threadID].pArg = NULL;
  619.     MTLeaveCritical();
  620.     MTSignal(semaCRunTimeLib);
  621.     GotoScheduler();
  622.   }
  623.   MTLeaveCritical();
  624.   MTSignal(semaCRunTimeLib);
  625. }
  626.  
  627.  
  628. /* Called by a thread to kill itself */
  629. void MTEndThread(void)
  630. {
  631.   MTKillThread(curThreadID);
  632. }
  633.  
  634.  
  635. /* Initialises the MicroThread system.
  636.    Needs to be called before anything else
  637.    or after MTEndMultiThreading()
  638.    for subsequent runs */
  639. void MTInitialise(void)
  640. {
  641.   int i;
  642.  
  643.   if (!bMTInitialised){
  644.     curThreadID = 0;
  645.     iCriticalSection=0;
  646.     bMultiThreading = 0;
  647.     bCtrlBreak=0;
  648.     bMTPreemptive=1;
  649.     returnCode=NORMAL;
  650.     for(i=0; i<MAX_THREADS; i++) {
  651.       threads[i].status = INVALID;
  652.       threads[i].semaphore = NULL;
  653.       threads[i].pOwnStack = NULL;
  654.       threads[i].ss=0;
  655.       threads[i].pFunc=NULL;
  656.       threads[i].pArg=NULL;
  657.       threads[i].pMsg=NULL;
  658.       threads[i].msgSize=0;
  659.       threads[i].nextID=0;
  660.       threads[i].prevID=0;
  661.     }
  662.     for(i=0; i<MAX_SEMAPHORES; i++) {
  663.       semaphores[i].value=0;
  664.       semaphores[i].inUse=0;
  665.       semaphores[i].QHead=0;
  666.       semaphores[i].QTail=0;
  667.     }
  668.     for(i=0; i<MAX_MAILBOXES; i++) {
  669.       mailboxes[i].name[0]='\0';
  670.       mailboxes[i].numSenders=0; /* this is incremented on each wait */
  671.       mailboxes[i].numReceivers=0; /* this is incremented on each wait */
  672.       mailboxes[i].inUse=0; /* Nonzero if in used, non-zero otherwise*/
  673.       mailboxes[i].QHead=0; /* head of queue of threads waiting to send to this mbx*/
  674.       mailboxes[i].QTail=0; /* tail of above queue */
  675.     }
  676.     for(i=1; i<=5; i++){
  677.       ReadyQHeads[i]=0;
  678.       ReadyQTails[i]=0;
  679.     }
  680.     sleepQHead=0;
  681.     sleepQTail=0;
  682.     bMTInitialised = 1;
  683.     if (MTAddNewThread((PThreadFunc)TheScheduler,0,0,NULL) != 0){
  684.       bMTInitialised = 0;
  685.       return;
  686.     }
  687.     schedulerSS = threads[0].ss;
  688.     schedulerSP = threads[0].sp;
  689.     semaCRunTimeLib=MTCreateSema();
  690.     clockTick=0L;
  691.   }
  692.   directvideo=1;
  693. }
  694.  
  695.  
  696.  
  697. /* Stops multi-threading - to be called by a running thread */
  698. void MTEndMultiThreading(void)
  699. {
  700.   if (bMultiThreading){
  701.     bMultiThreading = 0;
  702.     GotoScheduler();
  703.   }
  704. }
  705.  
  706.  
  707.  
  708. /* allocates a unique semaphore
  709.    id of 0 is used as an error condition */
  710. Semaphore MTCreateSema(void)
  711. {
  712.   unsigned i;
  713.   MTEnterCritical();
  714.   for (i=1; i<MAX_SEMAPHORES; i++){
  715.     if(semaphores[i].inUse==0){
  716.       semaphores[i].inUse=1;
  717.       MTLeaveCritical();
  718.       return i;
  719.     }
  720.   }
  721.   MTLeaveCritical();
  722.   return 0;
  723. }
  724.  
  725.  
  726. /* Frees a semaphore.
  727.    Freeing a semaphore still in use is an error! */
  728. unsigned MTDestroySema(Semaphore semaID)
  729. {
  730.   MTEnterCritical();
  731.   if(semaphores[semaID].value==0){ /* is it still in use? */
  732.     semaphores[semaID].inUse=0;
  733.     MTLeaveCritical();
  734.     return 1; /* No */
  735.   }
  736.   MTLeaveCritical();
  737.   return 0; /* Yes */
  738. }
  739.  
  740.  
  741.  
  742. /* A non-waiting version of MTWait. sets the semaphore only
  743.    if it is not set, otherwise it does nothing.
  744.    Returns 1 on success and 0 on failure.*/
  745. int MTTestAndSet(Semaphore semaID)
  746. {
  747.   int retval=0;
  748.   MTEnterCritical();
  749.   if (bMultiThreading && semaphores[semaID].inUse){
  750.       threads[curThreadID].semaphore = semaID;
  751.       if(semaphores[semaID].value==0){
  752.         semaphores[semaID].value++;
  753.         retval=1;
  754.       }
  755.   }
  756.   MTLeaveCritical();
  757.   return retval;
  758. }
  759.  
  760.  
  761. /* A thread must NOT wait for the same semaphore recursively */
  762. void MTWait(Semaphore semaID)
  763. {
  764.   MTEnterCritical();
  765.   if (bMultiThreading && semaphores[semaID].inUse){
  766.       threads[curThreadID].semaphore = semaID;
  767.       if(semaphores[semaID].value==0){
  768.         semaphores[semaID].value++;
  769.       }
  770.       else{
  771.         threads[curThreadID].status = WAITING;
  772.         EnqueueSema(curThreadID,semaID);
  773.         MTLeaveCritical();
  774.         GotoScheduler();
  775.       }
  776.   }
  777.   MTLeaveCritical();
  778. }
  779.  
  780.  
  781. /* Releases and signals that a semaphore is free */
  782. void MTSignal(Semaphore semaID)
  783. {
  784.   MTEnterCritical();
  785.   if (bMultiThreading && semaphores[semaID].inUse){
  786.       if(semaphores[semaID].value>0){
  787.         semaphores[semaID].value--;
  788.         WakeUpThreads(semaID);
  789.       }
  790.       MTLeaveCritical();
  791.       GotoScheduler();
  792.   }
  793.   MTLeaveCritical();
  794. }
  795.  
  796. /* Resume a thread waiting for a semaphore.
  797.    No real policy just FIFO */
  798. static void WakeUpThreads(unsigned semaID)
  799. {
  800.   unsigned threadID;
  801.  
  802.   if(semaphores[semaID].QHead){
  803.     threadID=DequeueSema(semaID);
  804.     threads[threadID].status=READY;
  805.     threads[threadID].semaphore=0;
  806.     EnqueueReady(threadID);
  807.   }
  808. }
  809.  
  810. /* Enter a critical section - freezes all other threads */
  811. unsigned MTEnterCritical(void)
  812. {
  813.   unsigned retval = 0;
  814.  
  815.   if (bMultiThreading){
  816.     iCriticalSection++;
  817.     retval = iCriticalSection;
  818.     if (iCriticalSection== MAX_CRITICAL_NESTING){
  819.       returnCode=TOO_DEEP_CRITICAL_NEST;
  820.       StopMultiThreading();
  821.     }
  822.   }
  823.   return retval;
  824. }
  825.  
  826.  
  827. /* Leaves critical section - resumes multithreading */
  828. void MTLeaveCritical(void)
  829. {
  830.   if (bMultiThreading && iCriticalSection > 0)
  831.     iCriticalSection--;
  832. }
  833.  
  834.  
  835. /* Elective yield for non-preemptive mode */
  836. void MTYield(void)
  837. {
  838.   if (bMultiThreading)
  839.     GotoScheduler();
  840. }
  841.  
  842.  
  843. /* Enables/disables preemption,
  844.    but not when running! */
  845. void MTSetPreemptive(int bPreempt)
  846. {
  847.   if ( !bMultiThreading)
  848.     bMTPreemptive = bPreempt ? 1 : 0;
  849. }
  850.  
  851.  
  852. /* Return the semaphore used to serialise access to
  853.    Turbo C's runtime libraries */
  854. Semaphore MTGetCRTLibSema(void)
  855. {
  856.   return semaCRunTimeLib;
  857. }
  858.  
  859. /* Returns the number of clock ticks since MTStartMultiThreading.
  860.    One second is approx. 18.2 clock ticks */
  861. unsigned long MTGetClockTick(void)
  862. {
  863.   return clockTick;
  864. }
  865.  
  866.  
  867. void printsleepq(void)
  868. {
  869.   unsigned tid=sleepQHead;
  870.   printf("\nHead(%u)=>",sleepQHead);
  871.   while(tid){
  872.     printf("(%u,%u,%u),",threads[tid].prevID,tid,threads[tid].nextID);
  873.     if(sleepQTail==tid)
  874.       printf("<=Tail(%u)",sleepQTail);
  875.     tid=threads[tid].nextID;
  876.   }
  877. }
  878.  
  879. /* Puts the current thread to sleep for a length of time */
  880. void MTSleep(unsigned long ticks)
  881. {
  882.    unsigned threadIdx, nextID;
  883.    unsigned long wakeUpTime;
  884.    MTEnterCritical();
  885.    wakeUpTime =clockTick + ticks;
  886.    threads[curThreadID].wakeUpTime=wakeUpTime;
  887.    threads[curThreadID].status=SLEEPING;
  888.    if (sleepQHead==0){
  889.      sleepQHead=sleepQTail=curThreadID;
  890.      threads[curThreadID].prevID=0;
  891.      threads[curThreadID].nextID=0;
  892.    }
  893.    else {
  894.      threadIdx=sleepQTail;
  895.      while(threadIdx!=sleepQHead
  896.        && threads[threadIdx].wakeUpTime > wakeUpTime){
  897.        threadIdx=threads[threadIdx].prevID;
  898.      }
  899.      if(threadIdx==sleepQHead
  900.        && threads[sleepQHead].wakeUpTime > wakeUpTime){
  901.        sleepQHead=curThreadID;
  902.        threads[curThreadID].prevID=0;
  903.        threads[curThreadID].nextID=threadIdx;
  904.        threads[threadIdx].prevID=curThreadID;
  905.      }
  906.      else{
  907.        if(threadIdx==sleepQTail){
  908.          sleepQTail=curThreadID;
  909.        }
  910.        threads[curThreadID].prevID=threadIdx;
  911.        threads[curThreadID].nextID=nextID=threads[threadIdx].nextID;
  912.        threads[threadIdx].nextID=curThreadID;
  913.        if(sleepQTail != curThreadID)
  914.          threads[nextID].prevID=curThreadID;
  915.      }
  916.    }
  917.    MTLeaveCritical();
  918.    GotoScheduler();
  919. }
  920.  
  921.  
  922. /* Checks and wakes up any sleeping thread that needs waking up */
  923. void WakeUpSleepingThreads(void)
  924. {
  925.   unsigned threadID;
  926.   while(sleepQHead && threads[sleepQHead].wakeUpTime <= clockTick){
  927.     threadID = DequeueThread(&sleepQHead, &sleepQTail);
  928.     threads[threadID].status=READY;
  929.     EnqueueReady(threadID);
  930.   }
  931. }
  932.  
  933.  
  934. /* allocates a mailbox given a name
  935.    id of 0 is used as an error condition */
  936. unsigned MTCreateMailbox(char *name)
  937. {
  938.   unsigned i,j;
  939.   MTEnterCritical();
  940.   for (i=1; i<MAX_MAILBOXES; i++){
  941.     if(mailboxes[i].inUse==0){
  942.       mailboxes[i].inUse=1;
  943.       for(j=0; j<MAILBOX_NAME_LEN; j++){
  944.         mailboxes[i].name[j]=name[j];
  945.         if (name[j]=='\0') break;
  946.       }
  947.       mailboxes[i].name[MAILBOX_NAME_LEN-1]='\0';
  948.       MTLeaveCritical();
  949.       return i;
  950.     }
  951.   }
  952.   MTLeaveCritical();
  953.   return 0;
  954. }
  955.  
  956. /* helper function */
  957. int StrMatch(char *str1, char *str2)
  958. {
  959.   int retval=1;
  960.   while (*str1 && *str2){
  961.     if(*str1!=*str2){
  962.       retval=0;
  963.       break;
  964.     }
  965.     str1++;
  966.     str2++;
  967.   }
  968.   return retval;
  969. }
  970.  
  971. unsigned MTGetMailboxHandle(char *mbxName)
  972. {
  973.   unsigned i;
  974.   for (i=1; i<MAX_MAILBOXES; i++){
  975.     if(mailboxes[i].inUse){
  976.       if(StrMatch(mbxName,mailboxes[i].name)){
  977.         return i;
  978.       }
  979.     }
  980.   }
  981.   return 0;
  982. }
  983.  
  984. int MTSendMsg(unsigned mailboxHnd, unsigned msgSize, void *pMsg)
  985. {
  986.     unsigned receiverId, copyMsgSize;
  987.     int retval=0;
  988.  
  989.     MTEnterCritical();
  990.     if(mailboxes[mailboxHnd].inUse){
  991.       /* Are there any receivers waiting on this mailbox/ */
  992.       if(mailboxes[mailboxHnd].numReceivers > 0){
  993.         /* if so, remove it from the queue */
  994.         receiverId=DequeueThread(&(mailboxes[mailboxHnd].QHead),
  995.                                  &(mailboxes[mailboxHnd].QTail));
  996.         /* check for size of receivers msg buffer being smaller */
  997.         copyMsgSize=msgSize;
  998.         if(threads[receiverId].msgSize < msgSize)
  999.           copyMsgSize=threads[receiverId].msgSize;
  1000.         /* transfer the msg */
  1001.         CopyMem(threads[receiverId].pMsg, pMsg, copyMsgSize);
  1002.         threads[receiverId].msgSize=copyMsgSize;
  1003.         /* put the receiver into the ready queue */
  1004.         threads[receiverId].status = READY;
  1005.         EnqueueReady(receiverId);
  1006.         mailboxes[mailboxHnd].numReceivers--;
  1007.         retval=copyMsgSize;
  1008.       }
  1009.       /* no receivers, wait in the queue */
  1010.       else {
  1011.         threads[curThreadID].pMsg = pMsg;
  1012.         threads[curThreadID].msgSize = msgSize;
  1013.         threads[curThreadID].status = WAITING;
  1014.         EnqueueThread(curThreadID,&(mailboxes[mailboxHnd].QHead),
  1015.                                   &(mailboxes[mailboxHnd].QTail));
  1016.         mailboxes[mailboxHnd].numSenders++;
  1017.         MTLeaveCritical();
  1018.         GotoScheduler();
  1019.         retval = threads[curThreadID].msgSize;
  1020.       }
  1021.     }
  1022.     MTLeaveCritical();
  1023.     return retval;
  1024. }
  1025.  
  1026.  
  1027. int MTReceiveMsg(unsigned mailboxHnd, unsigned bufferSize, void *pBuffer)
  1028. {
  1029.     unsigned senderId, copyMsgSize;
  1030.     int retval=0;
  1031.  
  1032.     MTEnterCritical();
  1033.     if(mailboxes[mailboxHnd].inUse){
  1034.       /* Are there any senders waiting on this mailbox/ */
  1035.       if(mailboxes[mailboxHnd].numSenders > 0){
  1036.         /* if so, remove it from the queue */
  1037.         senderId=DequeueThread(&(mailboxes[mailboxHnd].QHead),
  1038.                                &(mailboxes[mailboxHnd].QTail));
  1039.         /* check for size of receivers msg buffer being smaller */
  1040.         copyMsgSize=bufferSize;
  1041.         if(threads[senderId].msgSize < bufferSize)
  1042.           copyMsgSize=threads[senderId].msgSize;
  1043.         /* transfer the msg */
  1044.         CopyMem(pBuffer, threads[senderId].pMsg, copyMsgSize);
  1045.         retval = threads[senderId].msgSize=copyMsgSize;
  1046.         /* put the receiver into the ready queue */
  1047.         threads[senderId].status = READY;
  1048.         EnqueueReady(senderId);
  1049.         mailboxes[mailboxHnd].numSenders--;
  1050.       }
  1051.       /* no senders, wait in the queue */
  1052.       else {
  1053.         threads[curThreadID].pMsg = pBuffer;
  1054.         threads[curThreadID].msgSize = bufferSize;
  1055.         threads[curThreadID].status = WAITING;
  1056.         EnqueueThread(curThreadID,&(mailboxes[mailboxHnd].QHead),
  1057.                                   &(mailboxes[mailboxHnd].QTail));
  1058.         mailboxes[mailboxHnd].numReceivers++;
  1059.         MTLeaveCritical();
  1060.         GotoScheduler();
  1061.         retval = threads[curThreadID].msgSize;
  1062.       }
  1063.     }
  1064.     MTLeaveCritical();
  1065.     return retval;
  1066. }
  1067.  
  1068.  
  1069. unsigned MTDestroyMailbox(unsigned mailboxHnd)
  1070. {
  1071.   MTEnterCritical();
  1072.   if(mailboxes[mailboxHnd].numReceivers
  1073.     || mailboxes[mailboxHnd].numSenders){ /* is it still in use? */
  1074.     mailboxes[mailboxHnd].inUse=0;
  1075.     MTLeaveCritical();
  1076.     return 1; /* No */
  1077.   }
  1078.   MTLeaveCritical();
  1079.   return 0; /* Yes */
  1080. }
  1081.  
  1082.  
  1083.  
  1084.  
  1085.